package spark.network;

import org.apache.log4j.Logger;

import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.DatagramPacket;
import java.io.IOException;

import spark.network.protocol.Base;

public abstract class Demon implements Runnable {
	private static Logger logger = Logger.getLogger(Demon.class);

	private DatagramSocket socket;
	private ITimeAdjuster timeAdjuster;

	protected Demon(int queryPort) throws SocketException {
		socket = new DatagramSocket(queryPort);
	}

	protected Demon() throws SocketException {
		socket = new DatagramSocket();
	}

	protected abstract void processPacket(DatagramPacket packet, long receiveTime);

	@Override
	public void run() {
		byte[] buffer = Util.createBuffer();
		DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
		while(!socket.isClosed()) {
			long receiveTime;
			//receive protocol
			packet.setLength(buffer.length);
			try {
				socket.receive(packet);
				receiveTime = System.currentTimeMillis() + timeAdjuster.getTimeAdjustment();
			}
			catch(IOException e) {
				logger.error("Query udp socket closed, receive() interrupted - this is normal behavior on shutdown");
				return;
			}

			//process protocol if it is a valid one according to the protocol
			if(Base.isPrefixValid(packet.getData())) {
				processPacket(packet, receiveTime);
			}
		}
	}

	public void setTimeAdjuster(ITimeAdjuster timeAdjuster) {
		this.timeAdjuster = timeAdjuster;
	}

	/**
	 * @return Port of the socket used to receive packets on this demon. -1 if the socket is closed.
	 */
	public int getSocketPort() {
		return socket.getLocalPort();
	}

	public void shutdown() {
		if(!socket.isClosed()) {
			socket.close();
		}
	}
}